package com.trevor.collector.queue;

import com.sweet.logging.storage.StorageService;
import com.sweet.logging.storage.entity.LogMessage;
import com.trevor.collector.CollectorQueue;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.concurrent.*;

/**
 * <h1> Sweet 日志采集器队列</h1>
 * <p>
 * <b>描述：</b>
 * <br>
 * <br>
 * </p>
 *
 * @author xiexiaofeng
 * @version 1.0     2017年09月09日  09点04分
 */
public class SweetCollectorQueue implements CollectorQueue
{
    /**
     * 集群中，有的机器网速快，有的慢，有的性能高，有的低，可能导致先发生
     * 的日志，后送达。针对这种情况，需要在收集日志的时候，按照时间戳进行
     * 排序。
     */
    private volatile PriorityBlockingQueue<LogMessage> queue;

    private StorageService storageService; // 由spring注入
    private Executor       executor = null;

    private List<LogMessage> logMessages = new ArrayList<>(500); // 用于批量存储，提高效率

    public SweetCollectorQueue(int queueSize, StorageService storageService)
    {
        this.storageService = storageService;
        queue    = new PriorityBlockingQueue<>(queueSize, Comparator.comparingLong(LogMessage::getTimeStamp));
        executor = Executors.newSingleThreadExecutor();
        executor.execute(() ->
        {
            while (true)
            {
                try
                {
                    for (int i = 0; i < 500; i++)
                    {
                        logMessages.add(queue.take());
                    }
                    storageService.save(logMessages);
                    logMessages.clear();
                } catch (InterruptedException e)
                {
                    e.printStackTrace();
                }
            }
        });
    }

    @Override
    public void push(String remoteAddress, StringBuilder content)
    {
        // 日志进入队列
        if (content.length() > 10)
        {
            try
            {
                parseAndPut(remoteAddress, content);
            } catch (InterruptedException e)
            {
                e.printStackTrace();
            }
        }
    }

    /**
     * 解析对象
     * @param remoteAddress 客户端地址
     * @param content 数据
     */
    private void parseAndPut(String remoteAddress, StringBuilder content) throws InterruptedException
    {
        int spaceCount = 0; // 计算空格，按照约定，第三个空格之后是日志正文
        StringBuilder timeStamp = new StringBuilder();
        StringBuilder level     = new StringBuilder();
        StringBuilder name      = new StringBuilder();
        StringBuilder message   = new StringBuilder();
        for (int i = 0; i < content.length(); i++)
        {
            char c = content.charAt(i);
            if (c == ' ')
                spaceCount++;
            if (c == Character.MIN_VALUE)
            {
                queue.put(createLogMessage(remoteAddress, timeStamp, level, name, message));
                // 重新初始化变量
                spaceCount = 0;
                timeStamp.delete(0, timeStamp.length());
                level.delete(0, level.length());
                name.delete(0, name.length());
                message.delete(0, message.length());
                continue;
            }
            if (spaceCount == 0)
            {
                timeStamp.append(c); // 日志输出时间戳
            } else if (spaceCount == 1)
            {
                level.append(c); // 日志级别
            } else if (spaceCount == 2)
            {
                name.append(c); // 所在类名
            } else
            {
                message.append(c);
            }
        }
    }

    /**
     * 根据字符数据，反序列化出对象
     */
    private LogMessage createLogMessage(String remoteAddress,  StringBuilder timeStamp, StringBuilder level,
                                        StringBuilder name, StringBuilder message)
    {
        LogMessage logMessage = new LogMessage();
        // TODO timeStamp 的长度可能为0，暂时没分析出原因
        logMessage.setClient(clearRemoteAddress(remoteAddress));
        logMessage.setTimeStamp(Long.parseLong(timeStamp.toString()));
        logMessage.setLevel(level.toString());
        logMessage.setName(name.toString());
        logMessage.setMessage(message.toString());
        return logMessage;
    }

    /**
     * 简化下IP地址
     */
    private String clearRemoteAddress(String remoteAddress)
    {
        int startIndex = remoteAddress.startsWith("/") ? 1 : 0;
        int endIndex = remoteAddress.indexOf(":");
        return remoteAddress.substring(startIndex, endIndex > 0 ? endIndex : remoteAddress.length());
    }

    public void close()
    {

    }

}
